本文为您详细介绍呼叫中心SDK前端接入流程。
注意 当前版本仅做维护,后续不会继续迭代。如有需求,请参见热线SDK接入(新版)。
引用SDK
- SDK cdn assets(umd module)
- SDK Preview Sandbox
https://g.alicdn.com/xspace/phone/0.5.1/index.html#/sample/use-sdk
依赖
- SIM-API:https://g.alicdn.com/crm/sipml-api/0.0.8/SIPml-api.js。
- HTTP:https://g.alicdn.com/xspace/phone/0.5.1/http.js。
启用渲染拨号盘会自动加载以上依赖。加载后即可获取SDK实例内容:
const { SoftPhoneSDK } = window
前提条件
初始化前需要完成以下准备工作:
- 必须使用Chrome浏览器,版本号为58以上。原因是云呼叫中心的通话是通过webRTC技术实现的,目前Chrome浏览器对于webRTC技术的支持是最好的。为了保证您的通话质量及安全性,所以我们做出了这样的要求。
- 软电话SDK所嵌入的自有业务系统必须使用HTTPS协议。原因是Chrome在47版本之后,禁止HTTP协议获取系统麦克风权限,会造成无法正常通话。
- 如果您是在
iframe
标签内使用软电话SDK,那么需要为iframe
标签增加allow="microphone"
属性,来允许iframe
标签获取系统麦克风权限。
接入手册
状态流转
- 蓝色为流转状态触发的事件hook,请参见事件回调枚举。
- 白色为坐席状态,请参见坐席状态枚举。
- 加载SDK初始坐席状态为-1表示未注册,需要申请InstanceId BearerToken进行签入坐席操作。每个坐席原则意义上按1人/位,可能存在设置同一坐席公用。具体流程如下:
- 软电话类
const { SoftPhone } = window.SoftPhoneSDK; const softPhone = SoftPhone.getInstance(); // softphone follow Singleton Pattern
通过getInstance方法生成唯一实例。
- 实例API
API type description setConfig (config: Partial<SDKConfig> = {}) 注入配置,如下所示 register (eventName: EnumEventName, cb: (eventData: { type: EnumEventName, preConfig: Partial<SDKConfig>, data: any)} => void ) => Canceler 注册LifeCycle事件返回其注销方法。 getUIComponent () => React.ElementType 获取拨号条组件实例,配合APIs.renderComponent 渲染 使用getUIComponent获取的组件必须使用APIs.renderComponent去挂载,这是因为SDK内部集成了React&ReactDOM,使用外部版本挂载会导致版本不通或者双副本的问题。
- SDKConfig Options
option type defaultvalue description InstanceId string "" 实例ID,可在阿里云控制台使用主账号登录实例列表获取https://ccc.console.aliyun.com/AccInstance。 BearerToken string "" bearerToken,通过三方账号授权置换token中的access_token。 env "online" | "preOnline" 'online' 配置环境。 apiHost "scsp.aliyuncs.com" | "aiccs.aliyuncs.com" | "api.rhinokeen.com" 'scsp.aliyuncs.com' API服务器。 Version string '2020-07-02' POP API版本号。 - scsp.aliyun.com对应版本为
'2020-07-02'
- aiccs.aliyun.com对应版本为
'2019-10-15'
- api.rhinekeen.com无需版本
locale EnumLocale EnumLocale['zh-CN'] 国际化。 isPlugin boolean true 拨号条插件是否支持拖拽, 默认支持。 needDesensitize boolean false 是否对入呼或外呼号码脱敏展示,默认不脱敏。 autoChangeOnLineTime number 1 自定义话后处理时间,单位为s(秒),为0代表不需要自动切状态,默认为1s自动结束话后处理。 canChangeStatusByHand boolean false 是否能够手动切换状态的能力,默认为false,不展示操作栏&不支持手动更新。 enableVoiceToText Array<'callin' | 'callout'> [] 启用语音转文本,该能力需要BU配置支持。 enableServiceSummary boolean false 启用服务摘要。 disableUI boolean false 是否隐藏UI,默认为false,不隐藏。 cdnPath string //g.alicdn.com 内置资源默认引用CDN域。 - scsp.aliyun.com对应版本为
- Demo
const config = { InstanceId: 'ccc_xp_pre-cn-78v1gnp97002', // 实例id,可在阿里云控制台实例列表获取 BearerToken: '', // bearerToken,通过【三方账号授权】置换token中的access_token env: 'online', // online || preOnline, apiHost: 'api.rhinokeen.com', // scsp.aliyuncs.com || aiccs.aliyuncs.com || api.rhinokeen.com Version: '', // Pop Api 版本号,默认为'2020-07-02'. scsp.aliyun.com对应版本为'2020-07-02';aiccs.aliyun.com对应版本为'2019-10-15';api.rhinekeen.com无需版本 locale: EnumLocale['zh-CN'], isPlugin: true, // 插件是否支持拖拽,默认支持 needDesensitize: false, // 是否对入呼/外呼号码脱敏展示,默认不脱敏 autoChangeOnLineTime: 1, // 自定义话后处理时间,单位为s,为0代表不需要自动切状态,默认为1s自动结束话后处理 canChangeStatusByHand: false, // 是否能够手动切换状态 disableUI: false // 是否隐藏UI, 默认为false,不隐藏 }; softPhone.setConfig(config); softPhone.register(EnumEventName.actionError, () => { softPhone.setConfig({ BearerToken: 'newBearerToken' // token失效后,此处进行事件注册,设置新的token }); }); const SoftPhoneUI: React.ElementType<any> = softPhone.getUIComponent(); // if use React Framework, you can render component directly const ExampleApp: React.FC = () => { return ( <SoftPhoneUI /> ); } // if not use React Framework, you can render softphone to specified ElementNode APIs.renderComponent(SoftPhoneUI, document.querySelector('#phone-container'));
- 实例API
- LifeCycle事件注册
旨在各调度过程中触发的钩子,采用订阅派发的模式进行管理。具体流转状态,请参见流转状态流程图。
- 事件回调枚举
export enum EnumEventName { actionError = 'actionError', // 出错 actionSuccess = 'actionSuccess', // 拨号盘流程回调 onAgentCheckedin = 'onAgentCheckedin', // 上班签入成功回调 onAgentCheckedout = 'onAgentCheckedout', // 下班签出成功回调 onCallComing = 'onCallComing', // 新来电振铃回调 onCallDialOut = 'onCallDialOut', // 外拨请求中回调 onCallWillAnswer = 'onCallWillAnswer', // 呼入电话执行接听动作回调 onCallEstablishForCallin = 'onCallEstablishForCallin', // 呼入通话建立完成回调(等同于完成接听): onCallEstablishForCallout = 'onCallEstablishForCallout', // 呼出通话建立完成回调 onCallWillHangup = 'onCallWillHangup', // 执行挂断动作回调 onCallDidHangup = 'onCallDidHangup', // 呼入电话挂断完成回调 onCallOutDidHangup = 'onCallOutDidHangup', // 呼出电话挂断完成回调 onCallTransferOut = 'onCallTransferOut', // 转接回调 0.4.16版本新增 onCallHold = 'onCallHold', // 通话保持回调 0.4.16版本新增 onCallRecovery = 'onCallRecovery', // 通话恢复回调 0.4.16版本新增 'actionError.tokenExpired' = 'actionError.tokenExpired', // token失效回调 }
- 注册监听Demo
// register action error callback const unRegister = softPhone.register(EnumEventName['actionError.tokenExpired'], (eventData, prevConfig, data) => { // here! your can update BearerToken by softPhone.setConfig({BearerToken: 'xxx'}); const { type, data } = eventData; console.log('[SoftPhone actionError.tokenExpired]',type, data); }); unRegister() // unRegister current actionError.tokenExpired event
eventData字段说明:acid: string // 通话id channelId agentBasicCode: "AgentBusyForCall" // 代理状态Code agentBasicDesc: "坐席通话忙" // 代理状态描述 agentCallCode: "AgentCallDialOut" // 拨号状态Code agentCallDesc: "外拨中" // 拨号状态描述 aid: string // 在线id ani: string appName: string cmd: string connId: string // 话务Id departmentId: string dnis: string eventTime: string // 事件时间 holdConnId: "" jobId: string // 小二职位id mid: string // memberId? name: string // 会员名称 status: AgentBarStatus // 坐席状态AgentBarStatus subSessionId: string // 子会话id supportNewFunction: string time: string
- 事件回调枚举
- 静态APIs列表
const APIs = { renderComponent, // 渲染组件到指定节点 doCallOut, // 执行外呼 startWork, // 执行开始上班, 已废弃,可由agentCheckin替代 agentCheckin, // 执行拨号盘签入 agentCheckout, // 执行拨号盘签出 getAgentBarStatus, // 获取当前拨号盘状态 callAnswer, // 来电接听 callHangup, // 电话挂断 takeWebSocketNotify, // 获取 websocket 通道实例 }
- 坐席签入Demo登录操作,状态流转到AgentBarStatus.AgentCheckout。
const { APIs } = window.SoftPhoneSDK; APIs.agentCheckin(); // 是否成功可通过注册onAgentCheckedin事件来监听
- 坐席签出Demo登出操作,状态流转到 AgentBarStatus.AgentCheckin。
const { APIs } = window.SoftPhoneSDK; APIs.agentCheckout(); // 是否成功可通过注册onAgentCheckedout事件来监听
- 发起呼叫Demo调用外呼能力,外呼成功后执行AgentBarStatus.onCallDialOut,通过返回的eventData.data执行挂断操作。
const { APIs } = window.SoftPhoneSDK; APIs.doCallOut({ number, // 要呼叫的号码 onSuccess: this.onSuccess, // 外呼成功接通后的回调,参数为通话信息对象 param: { number, // 要呼叫的号码 memberId, // 必填, 匿名或未知用户可传-1 memberName, // 必填,用户名称,未知可传 '匿名会员' calloutNumber, // 选填, 主叫号码,如未填则默认为配置的第一个 fromSource: 'other_system_out', // 必填, hotlinebs_out || ticket_out || other_system_out (热线||工单||其他系统) taskId: this.props.common.taskId, // 如需查动作记录/服务记录时, channelId || caseId必传其中之一 channelId: this.props.channelId, // 会话id 可选 caseId: this.props.common.caseId, // 工单id } })
- 挂断电话Demo
APIs.callHangup(connId, jobId, acid, aid);
应用场景:呼入不接起挂断、呼入接起主动挂断、拨出未接/接起主动挂断。// 拨入挂断 softPhone.register(EnumEventName.AgentRinging, (eventData, prevConfig, data) => { const { data: { connId, jobId, acid, aid } } = eventData; APIs.callHangup(connId, jobId, acid, aid); }); // 拨出挂断 softPhone.register(EnumEventName.AgentCallDialOut, (eventData, prevConfig, data) => { const { data: { connId, jobId, acid, aid } } = eventData; APIs.callHangup(connId, jobId, acid, aid); });
- 接听来电Demo
softPhone.register(EnumEventName.AgentRinging, (eventData, prevConfig, data) => { // 参数可从 AgentRinging 的事件回调参数里取 const { data: { connId, jobId, acid, aid } } = eventData; APIs.callAnswer(connId, jobId, acid, aid); });
- 获取坐席状态Demo回坐席状态,对应值对应的状态如下,初始为-1未注册。
const { APIs } = window.SoftPhoneSDK; const status = await APIs.getAgentBarStatus(); enum AgentBarStatus { AgentCheckout = 1, // 签出 AgentCheckin = 2, // 签入 AgentReady = 3, // 空闲 AgentBreak = 4, // 小休 AgentCallRelease = 5, // 通话结束(挂断的时候) AgentAcw = 6, // 话后处理 AgentRinging = 7, // 振铃 AgentCallAnswerRequest = 8, // 接听请求中 AgentCallDialOut = 9, // 拨号请求中 AgentCallInboundEstablish = 10, // 呼入通话 AgentCallOutBoundEstablish = 11, // 呼出通话 AgentCallInternalBoundEstablish = 12, // 内部通话b打给c AgentCallConsultEstablish = 13, // 被动求助通话建立 AgentCallHeld = 14, // 通话保持 AgentCallConferenceWaitAnswer = 16, // 发起三方 AgentCallThirdConsult = 17, // 三方通话中 AgentThirdRetrieveRequest = 18, // 三方取回中 AgentCallConference = 19, // 会议 三方通话状态 }
- 获取webSocket通道当某些场景下我们需要定制能力时(e.g.自定义语音转文本能力),可以直接获取websocket通道,该对象提供了两个方法和一个websocket实例。
当前(0.4.16)已定的 sceneType有:type Subscribe = (eventName: string, callback: (msgData: any) => void) => void; type PhoneNotify = { listenerContainer: { [eventName: string]: Array<(msgObj: any) => void>; }; on: Subscribe; off: Subscribe; socket: ReconnectingWebSocket; }; const { APIs } = window.SoftPhoneSDK; const notify: PhoneNotify = APIs.takeWebSocketNotify(); function listenerHotlineMsg(msgData) { // 当通道返回的 data.sceneType === 'hotline-message' 时执行 } notify.on('hotline-message', listenerHotlineMsg); // 执行订阅 notify.off('hotline-message', listenerHotlineMsg); // 注销订阅
hotline-message
热线消息,表示通话状态和信息。voice-to-text
语音转文本,表示推送实时语音同声传译后的文本内容。
- 坐席签入Demo
- 多语言
- 已支持语言枚举:
export enum EnumLocale { en = 'en', 'zh-CN' = 'zh-CN', }
- 默认语言:
zh_CN
- 设置语言Demo:
const { SoftPhone, EnumLocale } = window.SoftPhoneSDK; const softPhone = SoftPhone.getInstance(); softPhone.setConfig({ locale: EnumLocale.en, // default = 'zh_CN' });
- 已支持语言枚举:
- 热线状态当我们想对热线中状态流转有更高的定制需求时,e.g.
- 在RTC通道(打开或关闭)时打点。
- 在通话恢复时同步文本语音信息等。
可以先获取到websocket实例,然后根据msgData.head.agentCallCode
参照EnumWSEventType
枚举热线状态进行定制逻辑处理。具体枚举:
e.g.export enum EnumWSEventType { // 上班状态 AgentCheckin = 'AgentCheckin', // 签入 AgentCheckout = 'AgentCheckout', // 签出 AgentReady = 'AgentReady', // 空闲 AgentBreak = 'AgentBreak', // 小休 AgentAcw = 'AgentAcw', // 话后处理 // 通话事件监听 AgentRinging = 'AgentRinging', // 来电 AgentCallInboundEstablish = 'AgentCallInboundEstablish', // 呼入电话建立 AgentCallOutBoundEstablish = 'AgentCallOutBoundEstablish', // 呼出通话建立 AgentCallDialOut = 'AgentCallDialOut', // 拨号呼出 AgentCallAnswerRequest = 'AgentCallAnswerRequest', // 呼入通话执行接听 AgentCallRelease = 'AgentCallRelease', // 坐席释放 AgentCallHeld = 'AgentCallHeld', // 通话保持 // 双步转 AgentCallConferenceWaitAnswer = 'AgentCallConferenceWaitAnswer', // 三方求助等待应答 AgentCallConference = 'AgentCallConference', // 会议中 AgentCallThirdConsult = 'AgentCallThirdConsult', // 三方求助通话中 AgentThirdRetrieveRequest = 'AgentThirdRetrieveRequest', // 三方取回请求中 AgentCallConsultThirdRetrieveReq = 'AgentCallConsultThirdRetrieveReq', // 三方求助三方取回请求中 AgentCallConsultEstablish = 'AgentCallConsultEstablish', // 三方求助电话建立 AgentCallInternalBoundEstablish = 'AgentCallInternalBoundEstablish', // 内部通话中 // RTC AgentJoinChannel = 'AgentJoinChannel', // 创建 RTC 连接 AgentLeaveChannel = 'AgentLeaveChannel' // 销毁 RTC 连接 }
const { APIs, EnumWSEventType } = window.SoftPhone; const notify: PhoneNotify = APIs.takeWebSocketNotify; function listenerHotlineMsg(msgData) { // 当通道返回的 data.sceneType === 'hotline-message' 时执行 if(_get(msgData, 'head.agentCallCode') === EnumWSEventType.AgentLeaveChannel) { // dosomething } } takeWebSocketNotify.on('hotline-message', listenerHotlineMsg); // 执行订阅
完整版Demo
export function APPControll(props: RouterCommonProps) {
const [hash, refresh] = useHash();
const { state: { config } } = useContext(Context); // 配置托管到context
const standWrap = useRef();
useRegisterHooks(props.history); // 注册钩子
useInitalIDPToken(hash); // fetch BearerToken assign to `context state` when hash changed
useDeepEffect(() => {
instance.setConfig(config);
// 检测到过期 regenerator token
const unRegister = instance.register(EnumEventName.actionError, refresh);
// 渲染拨号条
APIs.renderComponent(instance.getUIComponent(), standWrap.current);
// 执行签入操作
APIs.agentCheckin();
return () => {
APIs.agentCheckout();
unRegister();
};
}, [config]);
return (
<div>
<App />
<div ref={standWrap} className="statusbar" />
</div>
)
}
// e.g. use ReactHooks Synchronization status to Context
function useRegisterHooks(history: RouterCommonProps['history']) {
const { state, dispatch } = useContext(Context);
useEffect(() => {
softPhone.register(EnumEventName.onAgentCheckedin, e => {
safeConsole('成功登录', e);
dispatch({
type: EnumEventName.onAgentCheckedin,
payload: _get(e, 'data'),
});
});
softPhone.register(EnumEventName.onAgentCheckedout, e => {
safeConsole('成功登出', e);
dispatch({
type: EnumEventName.onAgentCheckedout,
payload: _get(e, 'data'),
});
});
softPhone.register(EnumEventName.onCallDialOut, e => {
safeConsole('外拨请求中', e);
// ...
});
softPhone.register(EnumEventName.onCallComing, e => {
safeConsole('新来电振铃', e);
// navigation to callpanel page...
// 暂不支持呼入方主动挂断的事件,绕一下轮询去侦测是否放开坐席
;(async function() {
while (true) {
await sleep(1000);
const status = await APIs.getAgentBarStatus();
if (status === agmentBarStatus.AgentReady) {
// 处理呼入方主动挂断逻辑...
return false;
}
}
})();
});
softPhone.register(EnumEventName.onCallWillAnswer, e => {
safeConsole('呼入电话执行接听动作', e);
});
softPhone.register(EnumEventName.onCallEstablishForCallin, e => {
safeConsole('已接听来电', e);
});
softPhone.register(EnumEventName.onCallEstablishForCallout, e => {
safeConsole('呼出通话建立完成', e);
});
softPhone.register(EnumEventName.onCallWillHangup, e => {
safeConsole('挂断回调', e);
// navigation to pre page before callin
});
softPhone.register(EnumEventName.onCallDidHangup, e => {
safeConsole('呼入电话挂断完成', e);
});
softPhone.register(EnumEventName.onCallOutDidHangup, e => {
safeConsole('呼出电话挂断完成', e);
});
softPhone.register(EnumEventName.onCallHold, (e) => {
safeConsole('通话保持', e);
});
softPhone.register(EnumEventName.onCallRecovery, () => {
safeConsole('通话恢复');
});
softPhone.register(EnumEventName.onCallTransferOut, () => {
safeConsole('转交');
});
softPhone.register(EnumEventName['actionError.tokenExpired'], e => {
safeConsole('token失效', e);
// refresh logic...
});
return () => /* hanlder cancel events... */
}, []);
}
版发布记录
0.5.0
语音转文本提供呼入呼出可选自开。
0.4.17
- 优化代码逻辑,移除非必要打印信息(sipml除外)
- 新增抛出三个钩子(保持、恢复、转交)
0.4.16
- 新增语音转文本插件。
- 新增服务摘要插件。
- 新增对外暴露websocket以支持定制功能。
- 在线Demo更新。
0.4.15
新增本地化归属
0.4.12
- 新增对外的接听和挂断API。
- 新增隐藏UI的配置参数。
0.4.10
- 归属地外化多tab不显示问题修复
- 增加初始化配置参数-autoChangeOnLineTime用来配置话后处理时间(默认为0,不触发)
- 增加初始化配置参数-canChangeStatusByHand用来配置是否隐藏手动操作状态栏的能力(默认为false,不需要隐藏)
0.4.9
- 号码列表支持“号码组外呼”
- 浮层层级提升至9999
- 拨打电话,上下班等操作提供“同步api”
- 增加“获取拨号盘状态”的前端api
- fuyun api token 过期事件修复
- 入呼、外呼归属地外化
0.4.7
- 拨号盘内部事件通信优化,弃用
msg-bus
- iconfont更新
0.4.6
Demo页增加cdnPath
的可配置化
0.4.5
- 增加对后端接口外呼的支持
- 支持在已加载SIPml-api的环境下依旧能够正常运行
- 修改默认外呼来源为
other_system_out
0.4.4
- 增加双步转UI调整
- 测试环境变量修改
0.4.3
- 交互体验优化
- 增加系统外呼透露
- 增加是否脱敏展现号码的配置项
- 增加是否可拖动的配置项
- Fuyun和Pop空入参优化
- 转交逻辑优化,加入咨询和会议逻辑(暂时隐藏)
- 部分已知bug修复